//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//

#include <windows.h>
#include <ceddk.h>
#include <types.h>
#include <string.h>
#include <stdio.h>
#include <tchar.h>
#include <nkintr.h>

#include "monahans.h"
#include "qci.h"
#include "qci_private.h"
#include "xllp_defs.h"
#include "xllp_ost.h"
#include "xllp_mfp_plat.h"
#include "xllp_gpio_plat.h"
#include "xllp_mfp.h"
#include "xllp_camera.h"
#include "xllp_camera_os_depend.h"
#include "xllp_ci.h"
#include "xllp_i2c.h"
#include "xllp_clkmgr.h"
#include "xllp_intc.h"
#include "Args.h"
#include "ioctl_cfg.h"
#include "gpx_xllp.h"

XLLP_GPIO_T *gpio_reg = NULL;
P_XLLP_VUINT32_T mfp_reg = NULL;
unsigned int clk_reg = 0;
unsigned int ost_reg = 0;
unsigned int ci_reg = 0;
XLLP_I2C_T *i2c_reg = NULL;
XLLP_INTC_T *intc_reg = NULL;

// to call back camera driver
typedef struct qci_callback_item_s
{
    capture_callback_t callback;
    ULONG user;
    frame_t** frame;
} qci_callback_item_t;

static qci_callback_item_t callbacks[MAX_CALLBACK_NUM];

static P_XLLP_MFP_RM_DB_ID_T mfp_rm_db = NULL;
void qci_dump_regs();

DWORD qci_sys_intr;
HANDLE qci_intr_event;
extern DWORD WINAPI qci_intr_thread();
void histogram_lut_init();

BOOL cgu_enable = 0;
BOOL lut_load();

// MFP configuration
static XLLP_UINT32_T mfp_list []={
                    XLLP_MFP_CIF_DD_0_OFFSET,           // CIF_DD<0>
                    XLLP_MFP_CIF_DD_1_OFFSET,           // CIF_DD<1>
                    XLLP_MFP_CIF_DD_2_OFFSET,           // CIF_DD<2>
                    XLLP_MFP_CIF_DD_3_OFFSET,           // CIF_DD<3>
                    XLLP_MFP_CIF_DD_4_OFFSET,           // CIF_DD<4>
                    XLLP_MFP_CIF_DD_5_OFFSET,           // CIF_DD<5>
                    XLLP_MFP_CIF_DD_6_OFFSET,           // CIF_DD<6>
                    XLLP_MFP_CIF_DD_7_OFFSET,           // CIF_DD<7>
                   // XLLP_MFP_CIF_DD_8_OFFSET,           // CIF_DD<8>
                   // XLLP_MFP_CIF_DD_9_OFFSET,           // CIF_DD<9>
                    XLLP_MFP_CIF_MCLK_OFFSET,           // CIF_MCLK
                    XLLP_MFP_CIF_PCLK_OFFSET,           // CIF_PCLK
                    XLLP_MFP_CIF_HSYNC_OFFSET,          // CIF_HSYNC
                    XLLP_MFP_CIF_VSYNC_OFFSET,          // CIF_VSYNC
                    XLLP_MFP_PIN_EOLIST_MARKER
                };

static XLLP_MFP_ALT_FN_T af_list []={
                    XLLP_MFP_CIF_DD_0_AF,           // CIF_DD<0>
                    XLLP_MFP_CIF_DD_1_AF,           // CIF_DD<1>
                    XLLP_MFP_CIF_DD_2_AF,           // CIF_DD<2>
                    XLLP_MFP_CIF_DD_3_AF,           // CIF_DD<3>
                    XLLP_MFP_CIF_DD_4_AF,           // CIF_DD<4>
                    XLLP_MFP_CIF_DD_5_AF,           // CIF_DD<5>
                    XLLP_MFP_CIF_DD_6_AF,           // CIF_DD<6>
                    XLLP_MFP_CIF_DD_7_AF,           // CIF_DD<7> 
                   // XLLP_MFP_CIF_DD_8_AF,           // CIF_DD<8>
                   // XLLP_MFP_CIF_DD_9_AF,           // CIF_DD<9>
                    XLLP_MFP_CIF_MCLK_AF,           // CIF_MCLK
                    XLLP_MFP_CIF_PCLK_AF,           // CIF_PCLK
                    XLLP_MFP_CIF_HSYNC_AF,          // CIF_HSYNC
                    XLLP_MFP_CIF_VSYNC_AF,          // CIF_VSYNC
                };

static XLLP_MFP_DRIVE_STRENGTH_T ds_list []={
                    XLLP_MFP_CIF_DS,           // CIF_DD<0>
                    XLLP_MFP_CIF_DS,           // CIF_DD<1>
                    XLLP_MFP_CIF_DS,           // CIF_DD<2>
                    XLLP_MFP_CIF_DS,           // CIF_DD<3>
                    XLLP_MFP_CIF_DS,           // CIF_DD<4>
                    XLLP_MFP_CIF_DS,           // CIF_DD<5>
                    XLLP_MFP_CIF_DS,           // CIF_DD<6>
                    XLLP_MFP_CIF_DS,           // CIF_DD<7>
                  //  XLLP_MFP_CIF_DS,           // CIF_DD<8>
                  //  XLLP_MFP_CIF_DS,           // CIF_DD<9>
                    XLLP_MFP_CIF_DS,           // CIF_MCLK
                    XLLP_MFP_CIF_DS,           // CIF_PCLK
                    XLLP_MFP_CIF_DS,           // CIF_HSYNC
                    XLLP_MFP_CIF_DS,           // CIF_VSYNC
                };

static BOOL init_mfp_resources();

BOOL init_pin_resources()
{
    UINT32 status;

    init_mfp_resources();

    status =  XllpMfpResourceManager_List(
                mfp_rm_db, 
                mfp_list, 
                XLLP_MFP_RM_ID_XLLP_CIF, 
                XLLP_SET);

    if (XLLP_STATUS_SUCCESS != status)
    {
        RETAILMSG(1, (TEXT("camera driver failed XllpMfpResourceManager_List()!\r\n")));
        return FALSE;
    }
    return TRUE;
}

BOOL init_pins()
{
    XllpMfpSetAfDs_List(
                mfp_reg, 
                mfp_list, 
                af_list, 
                ds_list);

    XllpMfpActivatePullUpDown_List (
                mfp_reg,
                mfp_list,
                XLLP_OFF);

    /* Call_GPX */
#if 0
    GPX_SetDirection(XLLP_GPIO_CAMERA_HI_PWDN, XLLP_GPIO_DIRECTION_OUT);
    GPX_SetDirection(XLLP_GPIO_CAMERA_LO_PWDN, XLLP_GPIO_DIRECTION_OUT);
#endif

    return TRUE;
}

#define MFP_REG_SIZE 0x700
#define ci_reg_SIZE 0x400

static BOOL init_resources(void)
{
    DWORD qci_irq = IRQ_QCI;
    int status;
    HANDLE thread;

    PHYSICAL_ADDRESS phy_addr;

    phy_addr.QuadPart = MONAHANS_BASE_REG_PA_INTC;
    intc_reg = (XLLP_INTC_T*)MmMapIoSpace(phy_addr, sizeof(XLLP_INTC_T), FALSE);

    phy_addr.QuadPart = MONAHANS_BASE_REG_PA_OST;
    ost_reg = (unsigned int)MmMapIoSpace(phy_addr, sizeof(XLLP_OST_T), FALSE);

    phy_addr.QuadPart = MONAHANS_BASE_REG_PA_CAMERA;
    ci_reg = (unsigned int)MmMapIoSpace(phy_addr, ci_reg_SIZE, FALSE);

    phy_addr.QuadPart = MONAHANS_BASE_REG_PA_CLKMGR;
    clk_reg = (unsigned int)MmMapIoSpace(phy_addr, sizeof(XLLP_CLKMGR_T), FALSE);

    phy_addr.QuadPart = MONAHANS_BASE_REG_PA_GPIO;
    gpio_reg = (XLLP_GPIO_T*)MmMapIoSpace(phy_addr, sizeof(XLLP_GPIO_T), FALSE);

    phy_addr.QuadPart = MONAHANS_BASE_REG_PA_MFP;
    mfp_reg = (P_XLLP_VUINT32_T)MmMapIoSpace(phy_addr, MFP_REG_SIZE, FALSE);

    phy_addr.QuadPart = MONAHANS_BASE_REG_PA_I2C;
    i2c_reg = (XLLP_I2C_T*)MmMapIoSpace(phy_addr, sizeof(XLLP_I2C_T), FALSE);

    status = KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &qci_irq, sizeof(UINT32), 
                             &qci_sys_intr, sizeof(UINT32), NULL);
    if (status != TRUE)
    {
        RETAILMSG(1, (TEXT("Error obtaining camera SYSINTR value %d!\n"), status));
        return FALSE;
    }

    qci_intr_event = CreateEvent(NULL, FALSE, FALSE,NULL);

    if (!InterruptInitialize(qci_sys_intr, qci_intr_event, NULL, 0)) 
    {
        RETAILMSG(1, (TEXT("Error initializing camera interrupt event!\n")));
        return FALSE;
    }

    histogram_lut_init();
    init_pin_resources();

    // Launch the Camera ISR
    thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)qci_intr_thread, 0, 0, NULL);
    CloseHandle(thread);
    return TRUE;
}

static void init_i2c()
{
    XllpClockEnable((XLLP_CLKMGR_T*)clk_reg, XLLP_CLK_I2C, XLLP_TRUE);

    i2c_reg->XLLP_ICR = XLLP_ICR_UR;
    XllpI2cInit(i2c_reg, (P_XLLP_VUINT32_T)mfp_reg, mfp_rm_db, 0);
}

#define I2C_MUTEX_NAME		TEXT("I2C_CONTROL")	

int i2c_write(XLLP_UINT8_T slaveAddr, const XLLP_UINT8_T * bytesBuf, int bytesCount)
{
    int status;
    XLLP_BOOL_T bSENDSTOP = XLLP_TRUE;
    static HANDLE i2c_mutex = NULL;

    if (i2c_mutex == NULL)
        i2c_mutex = CreateMutex(NULL, FALSE, I2C_MUTEX_NAME);

    if (i2c_mutex == NULL)
        RETAILMSG(1, (_T("qci:CreateMutex ERROR\r\n")));
    
    if (WaitForSingleObject(i2c_mutex, 3000) != WAIT_OBJECT_0)
        RETAILMSG(1, (TEXT("qci: Waiting for Single Object failed %d \r\n"),GetLastError()));

    status = XllpI2CWrite((P_XLLP_I2C_T)(i2c_reg), (P_XLLP_OST_T)(ost_reg), slaveAddr, bytesBuf, bytesCount, bSENDSTOP);

    if (status != XLLP_STATUS_SUCCESS) {
        RETAILMSG(1, (TEXT("qci: XllpI2CWrite failed %d\r\n"), status));
    }

    status = ReleaseMutex(i2c_mutex);
 
    if (!status) {
        RETAILMSG(1, (TEXT("qci: Release Mutex failed %d \r\n"),GetLastError()));
    }
    
    return status;
}

void power_on()
{
    unsigned char buffer[2];
    init_i2c();
    init_pins();
    qci_clock_enable(0);
    // camera setup micco
    
    // V_CAM_ANA(LDO6 = 2.50v) power config.
    buffer[0] = 0x13;  // LDO6,4,3
    buffer[1] = 0x0A;  // LDO6 = 2.50v, LDO4 = 1.80v, LDO3 = 2.80v
    i2c_write(0x34, buffer, 2);
    
    // V_CI(LDO15 = 1.80v) power config. 
    buffer[0] = 0x17;  // LDO6,4,3
    buffer[1] = 0x0B;  // LDO15 = 1.80v, LDO14 = 2.90v
    i2c_write(0x34, buffer, 2);
    
}

void qci_clock_enable(BOOL is_on)
{
    XllpClockEnable((XLLP_CLKMGR_T*)clk_reg, XLLP_CLK_CAMERA, is_on? XLLP_TRUE : XLLP_FALSE);
    if (is_on)
        XllpCIInit(ci_reg, clk_reg);
}

static BOOL init_mfp_resources()
{
    HANDLE m_MfpRMDbHandle;
    BOOL bFirstInit;
	
    // Claim memory-mapped file for sharing MfpRmDb information.
    m_MfpRMDbHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
                                        0, XLLP_MFP_RM_DB_SIZE, MFPRMDB_SHARED_FILENAME );

    if (NULL == m_MfpRMDbHandle)       
    {
        NKDbgPrintfW(TEXT("camera Fail to CreateFileMapping(MFPRMDB_SHARED_FILENAME)! \r\n"));
        return FALSE; //if failed
    } else {
        // We need to tell if it's first or sequent call of  CreateFileMapping(MFPRMDB_SHARED_FILENAME)
        if (ERROR_ALREADY_EXISTS != GetLastError()) {
            // The first initialization of this memory-mapped file.
            RETAILMSG(1, (TEXT("camera driver First call of CreateFileMapping(MFPRMDB_SHARED_FILENAME)! \r\n")));           
            bFirstInit = TRUE;        
        } else {
            // Memory-mapped file already existed.
            RETAILMSG(1, (TEXT("camera driver Sequent call of CreateFileMapping(MFPRMDB_SHARED_FILENAME)! \r\n")));
            bFirstInit = FALSE;
        }

        // Retrieve the local handle of the global shared memory-mapped file
        mfp_rm_db = (P_XLLP_MFP_RM_DB_ID_T) MapViewOfFile(m_MfpRMDbHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0 );
        if (NULL == mfp_rm_db )
        {
            NKDbgPrintfW(TEXT("camera driver Fail to MapViewOfFile()!\r\n"));
            return FALSE; //if failed
        }

        if (TRUE == bFirstInit) {
            // Need to Initialize RM 
            if (XLLP_STATUS_SUCCESS != XllpMfpResourceManagerInit(mfp_rm_db)) {
                  NKDbgPrintfW(TEXT("camera driver Fail to XllpMfpResourceManagerInit()!\r\n"));
                        return FALSE; //if failed
            }
        }
    }// end if (NULL == m_MfpRMDbHandle)

    return TRUE;
}

BOOL init_all()
{
    memset(callbacks, 0, sizeof(callbacks));

    if (init_resources() != TRUE)
        return FALSE;

    power_on();

    return TRUE;
}

// set qci pin signal interface
void qci_set_interface(qci_interface_t *intf)
{
    // Configure CI according to OV2620's hardware        
    // master parallel with 8 data pins
    XllpCISetMode(ci_reg, intf->mode, intf->data_width);
    
    // enable pixel clock(sensor will provide pclock) and master clock = 26MHZ
    XllpCISetClock(ci_reg, clk_reg, XLLP_TRUE, XLLP_TRUE, intf->clock);

    // data sample on rising and h,vsync active high
    XllpCISetPolarity(ci_reg, 
                      intf->pclk_polarity, 
                      intf->hsync_polarity, 
                      intf->vsync_polarity);
}

void qci_enable()
{
    XllpCISetFIFO(ci_reg, 0, XLLP_CI_FIFO_THL_32, XLLP_TRUE, XLLP_TRUE);

    XllpCIEnable(ci_reg);
}

void qci_disable()
{
    XllpCIDisable(ci_reg, ost_reg, XLLP_TRUE, XLLP_TRUE);
}

// select frame buffer chain
void qci_set_frame_format(format_t *format)
{
	set_current_frame_list(format);
}

void qci_set_master_timing(qci_master_timing_t* timing)
{
    XllpCIConfigureMP(ci_reg, timing->width, timing->height, &timing->timing);
}

static XLLP_CI_IMAGE_FORMAT get_ci_format(int format)
{
    switch (format)
    {
    case XLLP_CAMERA_IMAGE_FORMAT_RAW10:
        return XLLP_CI_RAW10;
    case XLLP_CAMERA_IMAGE_FORMAT_YCBCR422_PLANAR:
        return XLLP_CI_YCBCR422_PLANAR;
    default:
        return XLLP_CI_RAW10;
    }
}

// set qci input output format
void qci_set_image_format(int in_format, 
                          int out_format)
{
    XLLP_CI_CMU_USAGE cmu_usage = XLLP_CI_CICMR_DMODE_DISABLE;
    XLLP_CI_IMAGE_FORMAT ci_in_format = get_ci_format(in_format);
    XLLP_CI_IMAGE_FORMAT ci_out_format = get_ci_format(out_format);

    cgu_enable = 0;
    XllpCISetImageFormat(ci_reg, ci_in_format, ci_out_format);

    if (in_format == XLLP_CAMERA_IMAGE_FORMAT_RAW10)
    {
        XllpCICGUSetAddrMuxSelect(ci_reg, XLLP_CI_CGU_MUX_2_TO_9);
        if (out_format == XLLP_CAMERA_IMAGE_FORMAT_YCBCR422_PLANAR)
        {
            cgu_enable = 1;
            cmu_usage = XLLP_CI_CMU_OUTPUT_YUV;
        }
    }
    
    XllpCICMUEnable(ci_reg, cmu_usage);
}

void capture_start(int in_still_skips)
{
    qci_disable();
    XllpCIResetFIFO(ci_reg);

    XllpCIClearInterruptStatus(ci_reg, 0xFFFFFFFF);

    XllpCIDisableComplete(ci_reg);

    if (cgu_enable)
        if (lut_load())
	    XllpCICGUEnable(ci_reg, XLLP_TRUE);

    dma_load();

    qci_enable();
    still_skips = in_still_skips;
    XllpCISetInterruptMask(ci_reg, XLLP_CI_CICR0_INTERRUPT_MASK & (~XLLP_CI_INT_EOF) & (~XLLP_CI_INT_SOF));
}

void capture_stop()
{
    XllpCISetInterruptMask(ci_reg, XLLP_CI_CICR0_INTERRUPT_MASK);
    qci_disable();
    XllpCIResetFIFO(ci_reg);
	dma_save();
}

clear_interrupt()
{
    unsigned int reg;
    reg = *(unsigned int*)(ci_reg + XLLP_CISR);
    *(volatile unsigned int*)(ci_reg + XLLP_CISR) = reg;
}

frame_t *dma_frame_take();
void dma_frame_submit(frame_t *frame);

BOOL add_callback(callback_data_t* callback_data)
{
    ULONG index = callback_data->mode;

    if (index >= MAX_CALLBACK_NUM)
        return FALSE;

    callbacks[index].callback = callback_data->callback;
    callbacks[index].user = callback_data->user;
    callbacks[index].frame = callback_data->frame;
    return TRUE;
}

void callback(frame_t* frame)
{
    int i;

    for (i = 0; i < MAX_CALLBACK_NUM; i++)
    {
        if (callbacks[i].callback == NULL)
            continue;

        __try
        {
            *(callbacks[i].frame) = frame;
            callbacks[i].callback(callbacks[i].user, i);
        }
        __except( EXCEPTION_EXECUTE_HANDLER )
        {
            DEBUGMSG(ZONE_ERROR,(TEXT("Qcibus.DLL : frame %x callback exception\r\n"), frame));
        }
    }
}

void qci_dump_regs()
{
	unsigned int qci_base = (unsigned int)ci_reg;
	NKDbgPrintfW(L"qci_reg 0x%08x", ci_reg);

	NKDbgPrintfW(L"XLLP_CICR0 0x%08x", *(unsigned int*)(qci_base + XLLP_CICR0));
	NKDbgPrintfW(L"XLLP_CICR1 0x%08x", *(unsigned int*)(qci_base + XLLP_CICR1));
	NKDbgPrintfW(L"XLLP_CICR2 0x%08x", *(unsigned int*)(qci_base + XLLP_CICR2));
	NKDbgPrintfW(L"XLLP_CICR3 0x%08x", *(unsigned int*)(qci_base + XLLP_CICR3));
	NKDbgPrintfW(L"XLLP_CICR4 0x%08x", *(unsigned int*)(qci_base + XLLP_CICR4));

	NKDbgPrintfW(L"XLLP_CISR 0x%08x", *(unsigned int*)(qci_base + XLLP_CISR));
	NKDbgPrintfW(L"XLLP_CITOR 0x%08x", *(unsigned int*)(qci_base + XLLP_CITOR));
	NKDbgPrintfW(L"XLLP_CIBR0 0x%08x", *(unsigned int*)(qci_base + XLLP_CIBR0));
	NKDbgPrintfW(L"XLLP_CIBR1 0x%08x", *(unsigned int*)(qci_base + XLLP_CIBR1));
	NKDbgPrintfW(L"XLLP_CIBR2 0x%08x", *(unsigned int*)(qci_base + XLLP_CIBR2));
	NKDbgPrintfW(L"XLLP_CIBR3 0x%08x", *(unsigned int*)(qci_base + XLLP_CIBR3));
        
	NKDbgPrintfW(L"XLLP_CIFR0 0x%08x", *(unsigned int*)(qci_base + XLLP_CIFR0));
	NKDbgPrintfW(L"XLLP_CIFR1 0x%08x", *(unsigned int*)(qci_base + XLLP_CIFR1));	
	NKDbgPrintfW(L"XLLP_CIFSR 0x%08x", *(unsigned int*)(qci_base + XLLP_CIFSR));


	NKDbgPrintfW(L"XLLP_CIDCSR0 0x%08x", *(unsigned int*)(qci_base + XLLP_CIDCSR0));
	NKDbgPrintfW(L"XLLP_CIDCSR1 0x%08x", *(unsigned int*)(qci_base + XLLP_CIDCSR1));
	NKDbgPrintfW(L"XLLP_CIDCSR2 0x%08x", *(unsigned int*)(qci_base + XLLP_CIDCSR2));
	NKDbgPrintfW(L"XLLP_CIDCSR3 0x%08x", *(unsigned int*)(qci_base + XLLP_CIDCSR3));


	NKDbgPrintfW(L"XLLP_CIDBR0 0x%08x", *(unsigned int*)(qci_base + XLLP_CIDBR0));
	NKDbgPrintfW(L"XLLP_CIDBR1 0x%08x", *(unsigned int*)(qci_base + XLLP_CIDBR1));
	NKDbgPrintfW(L"XLLP_CIDBR2 0x%08x", *(unsigned int*)(qci_base + XLLP_CIDBR2));
	NKDbgPrintfW(L"XLLP_CIDBR3 0x%08x", *(unsigned int*)(qci_base + XLLP_CIDBR3));

	NKDbgPrintfW(L"XLLP_CIDADR0 0x%08x", *(unsigned int*)(qci_base + XLLP_CIDADR0));
	NKDbgPrintfW(L"XLLP_CITADR0 0x%08x", *(unsigned int*)(qci_base + XLLP_CITADR0));
	NKDbgPrintfW(L"XLLP_CISADR0 0x%08x", *(unsigned int*)(qci_base + XLLP_CISADR0));
	NKDbgPrintfW(L"XLLP_CICMD0 0x%08x", *(unsigned int*)(qci_base + XLLP_CICMD0));
        
	NKDbgPrintfW(L"XLLP_CIDADR1 0x%08x", *(unsigned int*)(qci_base + XLLP_CIDADR1));
	NKDbgPrintfW(L"XLLP_CITADR1 0x%08x", *(unsigned int*)(qci_base + XLLP_CITADR1));
	NKDbgPrintfW(L"XLLP_CISADR1 0x%08x", *(unsigned int*)(qci_base + XLLP_CISADR1));
	NKDbgPrintfW(L"XLLP_CICMD1 0x%08x", *(unsigned int*)(qci_base + XLLP_CICMD1));

	NKDbgPrintfW(L"XLLP_CIDADR2 0x%08x", *(unsigned int*)(qci_base + XLLP_CIDADR2));
	NKDbgPrintfW(L"XLLP_CITADR2 0x%08x", *(unsigned int*)(qci_base + XLLP_CITADR2));
	NKDbgPrintfW(L"XLLP_CISADR2 0x%08x", *(unsigned int*)(qci_base + XLLP_CISADR2));
	NKDbgPrintfW(L"XLLP_CICMD2 0x%08x", *(unsigned int*)(qci_base + XLLP_CICMD2));

	NKDbgPrintfW(L"XLLP_CIDADR3 0x%08x", *(unsigned int*)(qci_base + XLLP_CIDADR3));
	NKDbgPrintfW(L"XLLP_CITADR3 0x%08x", *(unsigned int*)(qci_base + XLLP_CITADR3));
	NKDbgPrintfW(L"XLLP_CISADR3 0x%08x", *(unsigned int*)(qci_base + XLLP_CISADR3));
	NKDbgPrintfW(L"XLLP_CICMD3 0x%08x", *(unsigned int*)(qci_base + XLLP_CICMD3));

	NKDbgPrintfW(L"XLLP_CICMR 0x%08x", *(unsigned int*)(qci_base + XLLP_CICMR));
        NKDbgPrintfW(L"XLLP_CICMC0 0x%08x", *(unsigned int*)(qci_base + XLLP_CICMC0));
        NKDbgPrintfW(L"XLLP_CICMC1 0x%08x", *(unsigned int*)(qci_base + XLLP_CICMC1));
        NKDbgPrintfW(L"XLLP_CICMC2 0x%08x", *(unsigned int*)(qci_base + XLLP_CICMC2));
        NKDbgPrintfW(L"XLLP_CICCR 0x%08x", *(unsigned int*)(qci_base + XLLP_CICCR));
}

dma_buf_t histogram_lut_buf;
int *histogram_lut_desc;
unsigned int histogram_lut_desc_phy;
char *lut = NULL;

void histogram_lut_init()
{
    unsigned int size;
    dma_buf_t *buf = &histogram_lut_buf;

    size = HISTOGRAM_LUT_BUF_SIZE + sizeof(XLLP_CI_DMAC_DESCRIPTOR_T);
    dma_buf_alloc(buf, size, 0);
    histogram_lut_desc = (int*)(buf->buf + HISTOGRAM_LUT_BUF_SIZE);
    histogram_lut_desc_phy = buf->phy_addr + HISTOGRAM_LUT_BUF_SIZE;
}

void histogram_lut_deinit()
{
    dma_buf_free(&histogram_lut_buf);
}

BOOL lut_load()
{
    XLLP_STATUS_T status;

    if (lut)
    {
        status = XllpCICGULoadLutRam(ci_reg, 
                                   ost_reg, 
                                   (unsigned int*)histogram_lut_buf.buf,
                                   histogram_lut_buf.phy_addr,
                                   (unsigned int*)histogram_lut_desc,
                                   histogram_lut_desc_phy,
                                   lut);

        return status == XLLP_STATUS_SUCCESS;
    }

    return FALSE;
}

BOOL get_histogram(int in_format, XLLP_CI_HSU_COLOR_TYPE color, unsigned int *size, unsigned int *sum)
{
    XLLP_CI_HSU_MUX_SEL_TYPE mux_select;

    switch (in_format)
    {
    case XLLP_CAMERA_IMAGE_FORMAT_RAW10:
        mux_select = XLLP_CI_HSU_MUX_1_TO_9;
        break;
    case XLLP_CAMERA_IMAGE_FORMAT_RAW9:
        mux_select = XLLP_CI_HSU_MUX_0_TO_8;
        break;
    default:
        mux_select = XLLP_CI_HSU_MUX_0_TO_7;
    }

    *size = (mux_select == XLLP_CI_HSU_MUX_0_TO_7)? 512 : 1024;

    return XllpCIHSUGetHistgram(ci_reg,
                                ost_reg,
                                color,
                                mux_select,
                                (unsigned int*)histogram_lut_buf.buf,
                                histogram_lut_buf.phy_addr,
                                (unsigned int*)histogram_lut_desc,
                                histogram_lut_desc_phy,
                                *size,
                                sum);
}

static unsigned char default_lut[] = {
    // RED LUT
    0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
    0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c,
    0x40, 0x44, 0x48, 0x4c, 0x50, 0x54, 0x58, 0x5c,
    0x60, 0x64, 0x68, 0x6c, 0x70, 0x74, 0x78, 0x7c,
    0x80, 0x84, 0x88, 0x8c, 0x90, 0x94, 0x98, 0x9c,
    0xa0, 0xa4, 0xa8, 0xac, 0xb0, 0xb4, 0xb8, 0xbc,
    0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc,
    0xe0, 0xe4, 0xe8, 0xec, 0xf0, 0xf4, 0xf8, 0xfc,

    // BLUE LUT
    0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
    0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c,
    0x40, 0x44, 0x48, 0x4c, 0x50, 0x54, 0x58, 0x5c,
    0x60, 0x64, 0x68, 0x6c, 0x70, 0x74, 0x78, 0x7c,
    0x80, 0x84, 0x88, 0x8c, 0x90, 0x94, 0x98, 0x9c,
    0xa0, 0xa4, 0xa8, 0xac, 0xb0, 0xb4, 0xb8, 0xbc,
    0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc,
    0xe0, 0xe4, 0xe8, 0xec, 0xf0, 0xf4, 0xf8, 0xfc,

    // GREEN LUT
    0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
    0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c,
    0x40, 0x44, 0x48, 0x4c, 0x50, 0x54, 0x58, 0x5c,
    0x60, 0x64, 0x68, 0x6c, 0x70, 0x74, 0x78, 0x7c,
    0x80, 0x84, 0x88, 0x8c, 0x90, 0x94, 0x98, 0x9c,
    0xa0, 0xa4, 0xa8, 0xac, 0xb0, 0xb4, 0xb8, 0xbc,
    0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc,
    0xe0, 0xe4, 0xe8, 0xec, 0xf0, 0xf4, 0xf8, 0xfc,
};

XLLP_CI_CMU_COE_MATRIX default_coe = 
{
    YUV_FLOAT_TO_INT(0.299), YUV_FLOAT_TO_INT(0.587), YUV_FLOAT_TO_INT(0.114),
    YUV_FLOAT_TO_INT(0.5), YUV_FLOAT_TO_INT(-0.41869), YUV_FLOAT_TO_INT(-0.08131),
    YUV_FLOAT_TO_INT(-0.16874), YUV_FLOAT_TO_INT(-0.33126), YUV_FLOAT_TO_INT(0.5)
};

// set the black level, color correction and gama correction
// if no configuration specified, default values will be used
void qci_image_proc_cfg(qci_image_proc_cfg_t* cfg)
{
    unsigned int black_level = 0;
    XLLP_CI_CMU_COE_MATRIX* coe = &default_coe;

    lut = default_lut;

    if (cfg)
    {
        cgu_enable = 1;

        black_level = cfg->black_level;
        coe = &cfg->coe;
        if (cfg->lut)
            lut = cfg->lut;
    }

    XllpCICGUSetBlackLevel(ci_reg, black_level);
    XllpCICMUSetColorCorrectionCoe(ci_reg, coe);
}